home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
fish
/
001-100
/
001-025
/
022
/
lemacs
/
display.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-17
|
31KB
|
1,294 lines
/*
* The functions in this file handle redisplay. There are two halves, the
* ones that update the virtual display screen, and the ones that make the
* physical display screen the same as the virtual display screen. These
* functions use hints that are left in the windows by the commands.
*
*/
#include <stdio.h>
#include "estruct.h"
#include "edef.h"
#define WFDEBUG 0 /* Window flag debug. */
typedef struct VIDEO {
short v_flag; /* Flags */
char v_text[1]; /* Screen data. */
} VIDEO;
#define VFCHG 0x0001 /* Changed flag */
#define VFEXT 0x0002 /* extended (beyond column 80) */
#define VFREV 0x0004 /* reverse video status */
#define VFREQ 0x0008 /* reverse video request */
int vtrow = 0; /* Row location of SW cursor */
int vtcol = 0; /* Column location of SW cursor */
int ttrow = HUGE; /* Row location of HW cursor */
int ttcol = HUGE; /* Column location of HW cursor */
int lbound = 0; /* leftmost column of current line
being displayed */
VIDEO **vscreen; /* Virtual screen. */
VIDEO **pscreen; /* Physical screen. */
/*
* Initialize the data structures used by the display code. The edge vectors
* used to access the screens are set up. The operating system's terminal I/O
* channel is set up. All the other things get initialized at compile time.
* The original window has "WFCHG" set, so that it will get completely
* redrawn on the first call to "update".
*/
vtinit()
{
register int i;
register VIDEO *vp;
char *malloc();
(*term.t_open)();
(*term.t_rev)(FALSE);
vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (vscreen == NULL)
exit(1);
pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (pscreen == NULL)
exit(1);
for (i = 0; i < term.t_nrow; ++i)
{
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
exit(1);
vp->v_flag = 0;
vscreen[i] = vp;
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
exit(1);
vp->v_flag = 0;
pscreen[i] = vp;
}
}
/*
* Clean up the virtual terminal system, in anticipation for a return to the
* operating system. Move down to the last line and clear it out (the next
* system prompt will be written in the line). Shut down the channel to the
* terminal.
*/
vttidy()
{
mlerase();
movecursor(term.t_nrow, 0);
(*term.t_close)();
}
/*
* Set the virtual cursor to the specified row and column on the virtual
* screen. There is no checking for nonsense values; this might be a good
* idea during the early stages.
*/
vtmove(row, col)
{
vtrow = row;
vtcol = col;
}
/*
* Write a character to the virtual screen. The virtual row and column are
* updated. If the line is too long put a "$" in the last column. This routine
* only puts printing characters into the virtual terminal buffers. Only
* column overflow is checked.
*/
vtputc(c)
int c;
{
register VIDEO *vp;
vp = vscreen[vtrow];
if (vtcol >= term.t_ncol) {
vtcol = (vtcol + 0x07) & ~0x07;
vp->v_text[term.t_ncol - 1] = '$';
} else if (c == '\t')
{
do
{
vtputc(' ');
}
while ((vtcol&0x07) != 0);
}
else if (c < 0x20 || c == 0x7F)
{
vtputc('^');
vtputc(c ^ 0x40);
}
else
vp->v_text[vtcol++] = c;
}
/* put a character to the virtual screen in an extended line. If we are
not yet on left edge, don't print it yet. check for overflow on
the right margin */
vtpute(c)
int c;
{
register VIDEO *vp;
vp = vscreen[vtrow];
if (vtcol >= term.t_ncol) {
vtcol = (vtcol + 0x07) & ~0x07;
vp->v_text[term.t_ncol - 1] = '$';
} else if (c == '\t')
{
do
{
vtpute(' ');
}
while (((vtcol + lbound)&0x07) != 0);
}
else if (c < 0x20 || c == 0x7F)
{
vtpute('^');
vtpute(c ^ 0x40);
}
else {
if (vtcol >= 0)
vp->v_text[vtcol] = c;
++vtcol;
}
}
/*
* Erase from the end of the software cursor to the end of the line on which
* the software cursor is located.
*/
vteeol()
{
register VIDEO *vp;
vp = vscreen[vtrow];
while (vtcol < term.t_ncol)
vp->v_text[vtcol++] = ' ';
}
/*
* Make sure that the display is right. This is a three part process. First,
* scan through all of the windows looking for dirty ones. Check the framing,
* and refresh the screen. Second, make sure that "currow" and "curcol" are
* correct for the current window. Third, make the virtual and physical
* screens the same.
*/
update()
{
register LINE *lp;
register WINDOW *wp;
register VIDEO *vp1;
register VIDEO *vp2;
register int i;
register int j;
register int c;
#if TYPEAH
if (typahead())
return(TRUE);
#endif
/* update the reverse video flags for any mode lines out there */
for (i = 0; i < term.t_nrow; ++i)
vscreen[i]->v_flag &= ~VFREQ;
#if REVSTA
wp = wheadp;
while (wp != NULL) {
vscreen[wp->w_toprow+wp->w_ntrows]->v_flag |= VFREQ;
wp = wp->w_wndp;
}
#endif
wp = wheadp;
while (wp != NULL)
{
/* Look at any window with update flags set on. */
if (wp->w_flag != 0)
{
/* If not force reframe, check the framing. */
if ((wp->w_flag & WFFORCE) == 0)
{
lp = wp->w_linep;
for (i = 0; i < wp->w_ntrows; ++i)
{
if (lp == wp->w_dotp)
goto out;
if (lp == wp->w_bufp->b_linep)
break;
lp = lforw(lp);
}
}
/* Not acceptable, better compute a new value for the line at the
* top of the window. Then set the "WFHARD" flag to force full
* redraw.
*/
i = wp->w_force;
if (i > 0)
{
--i;
if (i >= wp->w_ntrows)
i = wp->w_ntrows-1;
}
else if (i < 0)
{
i += wp->w_ntrows;
if (i < 0)
i = 0;
}
else
i = wp->w_ntrows/2;
lp = wp->w_dotp;
while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
{
--i;
lp = lback(lp);
}
wp->w_linep = lp;
wp->w_flag |= WFHARD; /* Force full. */
out:
/* Try to use reduced update. Mode line update has its own special
* flag. The fast update is used if the only thing to do is within
* the line editing.
*/
lp = wp->w_linep;
i = wp->w_toprow;
if ((wp->w_flag & ~WFMODE) == WFEDIT)
{
while (lp != wp->w_dotp)
{
++i;
lp = lforw(lp);
}
vscreen[i]->v_flag |= VFCHG;
vtmove(i, 0);
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j));
vteeol();
}
else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
{
while (i < wp->w_toprow+wp->w_ntrows)
{
vscreen[i]->v_flag |= VFCHG;
vtmove(i, 0);
/* if line has been changed */
if (lp != wp->w_bufp->b_linep)
{
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j));
lp = lforw(lp);
}
vteeol();
++i;
}
}
#if ~WFDEBUG
if ((wp->w_flag&WFMODE) != 0)
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
}
#if WFDEBUG
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
/* and onward to the next window */
wp = wp->w_wndp;
}
/* Always recompute the row and column number of the hardware cursor. This
* is the only update for simple moves.
*/
lp = curwp->w_linep;
currow = curwp->w_toprow;
while (lp != curwp->w_dotp)
{
++currow;
lp = lforw(lp);
}
curcol = 0;
i = 0;
while (i < curwp->w_doto)
{
c = lgetc(lp, i++);
if (c == '\t')
curcol |= 0x07;
else if (c < 0x20 || c == 0x7F)
++curcol;
++curcol;
}
if (curcol >= term.t_ncol - 1) { /* extended line. */
/* flag we are extended and changed */
vscreen[currow]->v_flag |= VFEXT | VFCHG;
updext(); /* and output extended line */
} else
lbound = 0; /* not extended line */
/* make sure no lines need to be de-extended because the cursor is
no longer on them */
wp = wheadp;
while (wp != NULL) {
lp = wp->w_linep;
i = wp->w_toprow;
while (i < wp->w_toprow + wp->w_ntrows) {
if (vscreen[i]->v_flag & VFEXT) {
/* always flag extended lines as changed */
vscreen[i]->v_flag |= VFCHG;
if ((wp != curwp) || (lp != wp->w_dotp) ||
(curcol < term.t_ncol - 1)) {
vtmove(i, 0);
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j));
vteeol();
/* this line no longer is extended */
vscreen[i]->v_flag &= ~VFEXT;
}
}
lp = lforw(lp);
++i;
}
/* and onward to the next window */
wp = wp->w_wndp;
}
/* Special hacking if the screen is garbage. Clear the hardware screen,
* and update your copy to agree with it. Set all the virtual screen
* change bits, to force a full update.
*/
if (sgarbf != FALSE)
{
for (i = 0; i < term.t_nrow; ++i)
{
vscreen[i]->v_flag |= VFCHG;
vp1 = pscreen[i];
for (j = 0; j < term.t_ncol; ++j)
vp1->v_text[j] = ' ';
}
movecursor(0, 0); /* Erase the screen. */
(*term.t_eeop)();
sgarbf = FALSE; /* Erase-page clears */
mpresf = FALSE; /* the message area. */
}
/* Make sure that the physical and virtual displays agree. Unlike before,
* the "updateline" code is only called with a line that has been updated
* for sure.
*/
for (i = 0; i < term.t_nrow; ++i)
{
vp1 = vscreen[i];
/* for each line that needs to be updated, or that needs its
reverse video status changed, call the line updater */
j = vp1->v_flag;
if (((j & VFCHG) != 0) || (((j & VFREV) == 0) != ((j & VFREQ) == 0)))
{
#if TYPEAH
if (typahead())
return(TRUE);
#endif
vp2 = pscreen[i];
updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
}
}
/* Finally, update the hardware cursor and flush out buffers. */
movecursor(currow, curcol - lbound);
(*term.t_flush)();
}
/* updext: update the extended line which the cursor is currently
on at a column greater than the terminal width. The line
will be scrolled right or left to let the user see where
the cursor is
*/
updext()
{
register int rcursor; /* real cursor location */
register LINE *lp; /* pointer to current line */
register int j; /* index into line */
/* calculate what column the real cursor will end up in */
rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
lbound = curcol - rcursor + 1;
/* scan through the line outputing characters to the virtual screen */
/* once we reach the left edge */
vtmove(currow, -lbound); /* start scanning offscreen */
lp = curwp->w_dotp; /* line to output */
for (j=0; j<llength(lp); ++j) /* until the end-of-line */
vtpute(lgetc(lp, j));
/* truncate the virtual line */
vteeol();
/* and put a '$' in column 1 */
vscreen[currow]->v_text[0] = '$';
}
/*
* Update a single line. This does not know how to use insert or delete
* character sequences; we are using VT52 functionality. Update the physical
* row and column variables. It does try an exploit erase to end of line. The
* RAINBOW version of this routine uses fast video.
*/
updateline(row, vline, pline, flags)
char vline[]; /* what we want it to end up as */
char pline[]; /* what it looks like now */
short *flags; /* and how we want it that way */
{
#if RAINBOW
register char *cp1;
register char *cp2;
register int nch;
/* since we don't know how to make the rainbow do this, turn it off */
flags &= (~VFREV & ~VFREQ);
cp1 = &vline[0]; /* Use fast video. */
cp2 = &pline[0];
putline(row+1, 1, cp1);
nch = term.t_ncol;
do
{
*cp2 = *cp1;
++cp2;
++cp1;
}
while (--nch);
*flags &= ~VFCHG;
#else
register char *cp1;
register char *cp2;
register char *cp3;
register char *cp4;
register char *cp5;
register int nbflag; /* non-blanks to the right flag? */
int rev; /* reverse video flag */
int req; /* reverse video request flag */
/* set up pointers to virtual and physical lines */
cp1 = &vline[0];
cp2 = &pline[0];
#if REVSTA
/* if we need to change the reverse video status of the
current line, we need to re-write the entire line */
rev = *flags & VFREV;
req = *flags & VFREQ;
if (rev != req) {
movecursor(row, 0); /* Go to start of line. */
(*term.t_rev)(req != FALSE); /* set rev video if needed */
/* scan through the line and dump it to the screen and
the virtual screen array */
cp3 = &vline[term.t_ncol];
while (cp1 < cp3) {
(*term.t_putchar)(*cp1);
++ttcol;
*cp2++ = *cp1++;
}
(*term.t_rev)(FALSE); /* turn rev video off */
/* update the needed flags */
*flags &= ~VFCHG;
if (req)
*flags |= VFREV;
else
*flags &= ~VFREV;
return(TRUE);
}
#endif
/* advance past any common chars at the left */
while (cp1 != &vline[term.t_ncol] && cp1[0] == cp2[0]) {
++cp1;
++cp2;
}
/* This can still happen, even though we only call this routine on changed
* lines. A hard update is always done when a line splits, a massive
* change is done, or a buffer is displayed twice. This optimizes out most
* of the excess updating. A lot of computes are used, but these tend to
* be hard operations that do a lot of update, so I don't really care.
*/
/* if both lines are the same, no update needs to be done */
if (cp1 == &vline[term.t_ncol])
return(TRUE);
/* find out if there is a match on the right */
nbflag = FALSE;
cp3 = &vline[term.t_ncol];
cp4 = &pline[term.t_ncol];
while (cp3[-1] == cp4[-1]) {
--cp3;
--cp4;
if (cp3[0] != ' ') /* Note if any nonblank */
nbflag = TRUE; /* in right match. */
}
cp5 = cp3;
if (nbflag == FALSE && eolexist == TRUE) { /* Erase to EOL ? */
while (cp5!=cp1 && cp5[-1]==' ')
--cp5;
if (cp3-cp5 <= 3) /* Use only if erase is */
cp5 = cp3; /* fewer characters. */
}
movecursor(row, cp1-&vline[0]); /* Go to start of line. */
while (cp1 != cp5) { /* Ordinary. */
(*term.t_putchar)(*cp1);
++ttcol;
*cp2++ = *cp1++;
}
if (cp5 != cp3) { /* Erase. */
(*term.t_eeol)();
while (cp1 != cp3)
*cp2++ = *cp1++;
}
*flags &= ~VFCHG; /* flag this line is changed */
#endif
}
/*
* Redisplay the mode line for the window pointed to by the "wp". This is the
* only routine that has any idea of how the modeline is formatted. You can
* change the modeline format by hacking at this routine. Called by "update"
* any time there is a dirty window.
*/
modeline(wp)
WINDOW *wp;
{
register char *cp;
register int c;
register int n; /* cursor position count */
register BUFFER *bp;
register i; /* loop index */
register lchar; /* character to draw line in buffer with */
register firstm; /* is this the first mode? */
char tline[NLINE]; /* buffer for part of mode line */
n = wp->w_toprow+wp->w_ntrows; /* Location. */
vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
vtmove(n, 0); /* Seek to right line. */
if (wp == curwp) /* mark the current buffer */
lchar = '=';
else
#if REVSTA
if (revexist)
lchar = ' ';
else
#endif
lchar = '-';
vtputc(lchar);
bp = wp->w_bufp;
if ((bp->b_flag&BFCHG) != 0) /* "*" if changed. */
vtputc('*');
else
vtputc(lchar);
n = 2;
strcpy(tline, " MicroEMACS 3.6 ("); /* Buffer name. */
/* display the modes */
firstm = TRUE;
for (i = 0; i < NUMMODES; i++) /* add in the mode flags */
if (wp->w_bufp->b_mode & (1 << i)) {
if (firstm != TRUE)
strcat(tline, " ");
firstm = FALSE;
strcat(tline, modename[i]);
}
strcat(tline,") ");
cp = &tline[0];
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
vtputc(lchar);
vtputc(lchar);
vtputc(' ');
n += 3;
cp = &bp->b_bname[0];
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
vtputc(' ');
vtputc(lchar);
vtputc(lchar);
n += 3;
if (bp->b_fname[0] != 0) /* File name. */
{
vtputc(' ');
++n;
cp = "File: ";
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
cp = &bp->b_fname[0];
while ((c = *cp++) != 0)
{
vtputc(c);
++n;
}
vtputc(' ');
++n;
}
#if WFDEBUG
vtputc(lchar);
vtputc((wp->w_flag&WFMODE)!=0 ? 'M' : lchar);
vtputc((wp->w_flag&WFHARD)!=0 ? 'H' : lchar);
vtputc((wp->w_flag&WFEDIT)!=0 ? 'E' : lchar);
vtputc((wp->w_flag&WFMOVE)!=0 ? 'V' : lchar);
vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : lchar);
n += 6;
#endif
while (n < term.t_ncol) /* Pad to full width. */
{
vtputc(lchar);
++n;
}
}
upmode() /* update all the mode lines */
{
register WINDOW *wp;
wp = wheadp;
while (wp != NULL) {
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
}
/*
* Send a command to the terminal to move the hardware cursor to row "row"
* and column "col". The row and column arguments are origin 0. Optimize out
* random calls. Update "ttrow" and "ttcol".
*/
movecursor(row, col)
{
if (row!=ttrow || col!=ttcol)
{
ttrow = row;
ttcol = col;
(*term.t_move)(row, col);
}
}
/*
* Erase the message line. This is a special routine because the message line
* is not considered to be part of the virtual screen. It always works
* immediately; the terminal buffer is flushed via a call to the flusher.
*/
mlerase()
{
int i;
movecursor(term.t_nrow, 0);
if (eolexist == TRUE)
(*term.t_eeol)();
else {
for (i = 0; i < term.t_ncol - 1; i++)
(*term.t_putchar)(' ');
movecursor(term.t_nrow, 1); /* force the move! */
movecursor(term.t_nrow, 0);
}
(*term.t_flush)();
mpresf = FALSE;
}
/*
* Ask a yes or no question in the message line. Return either TRUE, FALSE, or
* ABORT. The ABORT status is returned if the user bumps out of the question
* with a ^G. Used any time a confirmation is required.
*/
mlyesno(prompt)
char *prompt;
{
char c; /* input character */
char buf[NPAT]; /* prompt to user */
for (;;) {
/* build and prompt the user */
strcpy(buf, prompt);
strcat(buf, " [y/n]? ");
mlwrite(buf);
/* get the responce */
c = (*term.t_getchar)();
if (c == BELL) /* Bail out! */
return(ABORT);
if (c=='y' || c=='Y')
return(TRUE);
if (c=='n' || c=='N')
return(FALSE);
}
}
/*
* Write a prompt into the message line, then read back a response. Keep
* track of the physical position of the cursor. If we are in a keyboard
* macro throw the prompt away, and return the remembered response. This
* lets macros run at full speed. The reply is always terminated by a carriage
* return. Handle erase, kill, and abort keys.
*/
mlreply(prompt, buf, nbuf)
char *prompt;
char *buf;
{
return(mlreplyt(prompt,buf,nbuf,'\n'));
}
/* A more generalized prompt/reply function allowing the caller
to specify the proper terminator. If the terminator is not
a return ('\n') it will echo as "<NL>"
*/
mlreplyt(prompt, buf, nbuf, eolchar)
char *prompt;
char *buf;
char eolchar;
{
register int cpos;
register int i;
register int c;
cpos = 0;
if (kbdmop != NULL) {
while ((c = *kbdmop++) != '\0')
buf[cpos++] = c;
buf[cpos] = 0;
if (buf[0] == 0)
return(FALSE);
return(TRUE);
}
/* check to see if we are executing a command line */
if (clexec) {
nxtarg(buf);
return(TRUE);
}
mlwrite(prompt);
for (;;) {
/* get a character from the user. if it is a <ret>, change it
to a <NL> */
c = (*term.t_getchar)();
if (c == 0x0d)
c = '\n';
if (c == eolchar) {
buf[cpos++] = 0;
if (kbdmip != NULL) {
if (kbdmip+cpos > &kbdm[NKBDM-3]) {
ctrlg(FALSE, 0);
(*term.t_flush)();
return(ABORT);
}
for (i=0; i<cpos; ++i)
*kbdmip++ = buf[i];
}
(*term.t_putchar)('\r');
ttcol = 0;
(*term.t_flush)();
if (buf[0] == 0)
return(FALSE);
return(TRUE);
} else if (c == 0x07) { /* Bell, abort */
(*term.t_putchar)('^');
(*term.t_putchar)('G');
ttcol += 2;
ctrlg(FALSE, 0);
(*term.t_flush)();
return(ABORT);
} else if (c == 0x7F || c == 0x08) { /* rubout/erase */
if (cpos != 0) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
if (buf[--cpos] < 0x20) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
}
if (buf[cpos] == '\n') {
(*term.t_putchar)('\b');
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
(*term.t_putchar)('\b');
--ttcol;
--ttcol;
}
(*term.t_flush)();
}
} else if (c == 0x15) { /* C-U, kill */
while (cpos != 0) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
if (buf[--cpos] < 0x20) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
}
}
(*term.t_flush)();
} else {
if (cpos < nbuf-1) {
buf[cpos++] = c;
if ((c < ' ') && (c != '\n')) {
(*term.t_putchar)('^');
++ttcol;
c ^= 0x40;
}
if (c != '\n')
(*term.t_putchar)(c);
else { /* put out <NL> for <ret> */
(*term.t_putchar)('<');
(*term.t_putchar)('N');
(*term.t_putchar)('L');
(*term.t_putchar)('>');
ttcol += 3;
}
++ttcol;
(*term.t_flush)();
}
}
}
}
/*
* Write a message into the message line. Keep track of the physical cursor
* position. A small class of printf like format items is handled. Assumes the
* stack grows down; this assumption is made by the "++" in the argument scan
* loop. Set the "message line" flag TRUE.
*/
mlwrite(fmt, arg)
char *fmt;
{
register int c;
register char *ap;
if (eolexist == FALSE) {
mlerase();
(*term.t_flush)();
}
movecursor(term.t_nrow, 0);
ap = (char *) &arg;
while ((c = *fmt++) != 0) {
if (c != '%') {
(*term.t_putchar)(c);
++ttcol;
}
else
{
c = *fmt++;
switch (c) {
case 'd':
mlputi(*(int *)ap, 10);
ap += sizeof(int);
break;
case 'o':
mlputi(*(int *)ap, 8);
ap += sizeof(int);
break;
case 'x':
mlputi(*(int *)ap, 16);
ap += sizeof(int);
break;
case 'D':
mlputli(*(long *)ap, 10);
ap += sizeof(long);
break;
case 's':
mlputs(*(char **)ap);
ap += sizeof(char *);
break;
default:
(*term.t_putchar)(c);
++ttcol;
}
}
}
if (eolexist == TRUE)
(*term.t_eeol)();
(*term.t_flush)();
mpresf = TRUE;
}
/*
* Write out a string. Update the physical cursor position. This assumes that
* the characters in the string all have width "1"; if this is not the case
* things will get screwed up a little.
*/
mlputs(s)
char *s;
{
register int c;
while ((c = *s++) != 0)
{
(*term.t_putchar)(c);
++ttcol;
}
}
/*
* Write out an integer, in the specified radix. Update the physical cursor
* position. This will not handle any negative numbers; maybe it should.
*/
mlputi(i, r)
{
register int q;
static char hexdigits[] = "0123456789ABCDEF";
if (i < 0)
{
i = -i;
(*term.t_putchar)('-');
}
q = i/r;
if (q != 0)
mlputi(q, r);
(*term.t_putchar)(hexdigits[i%r]);
++ttcol;
}
/*
* do the same except as a long integer.
*/
mlputli(l, r)
long l;
{
register long q;
if (l < 0)
{
l = -l;
(*term.t_putchar)('-');
}
q = l/r;
if (q != 0)
mlputli(q, r);
(*term.t_putchar)((int)(l%r)+'0');
++ttcol;
}
#if RAINBOW
putline(row, col, buf)
int row, col;
char buf[];
{
int n;
n = strlen(buf);
if (col + n - 1 > term.t_ncol)
n = term.t_ncol - col + 1;
Put_Data(row, col, n, buf);
}
#endif
/* get a command name from the command line. Command completion means
that pressing a <SPACE> will attempt to complete an unfinished command
name if it is unique.
*/
int (*getname())()
{
register int cpos; /* current column on screen output */
register int c;
register char *sp; /* pointer to string for output */
register NBIND *ffp; /* first ptr to entry in name binding table */
register NBIND *cffp; /* current ptr to entry in name binding table */
register NBIND *lffp; /* last ptr to entry in name binding table */
char buf[NSTRING]; /* buffer to hold tentative command name */
int (*fncmatch())();
/* starting at the begining of the string buffer */
cpos = 0;
/* if we are executing a keyboard macro, fill our buffer from there,
and attempt a straight match */
if (kbdmop != NULL) {
while ((c = *kbdmop++) != '\0')
buf[cpos++] = c;
buf[cpos] = 0;
/* return the result of a match */
return(fncmatch(&buf[0]));
}
/* if we are executing a command line get the next arg and match it */
if (clexec) {
nxtarg(buf);
return(fncmatch(&buf[0]));
}
/* build a name string from the keyboard */
while (TRUE) {
c = (*term.t_getchar)();
/* if we are at the end, just match it */
if (c == 0x0d) {
buf[cpos] = 0;
/* save keyboard macro string if needed */
if (kbdtext(&buf[0]) == ABORT)
return( (int (*)()) NULL);
/* and match it off */
return(fncmatch(&buf[0]));
} else if (c == 0x07) { /* Bell, abort */
(*term.t_putchar)('^');
(*term.t_putchar)('G');
ttcol += 2;
ctrlg(FALSE, 0);
(*term.t_flush)();
return( (int (*)()) NULL);
} else if (c == 0x7F || c == 0x08) { /* rubout/erase */
if (cpos != 0) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
--cpos;
(*term.t_flush)();
}
} else if (c == 0x15) { /* C-U, kill */
while (cpos != 0) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--cpos;
--ttcol;
}
(*term.t_flush)();
} else if (c == ' ') {
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
/* attempt a completion */
buf[cpos] = 0; /* terminate it for us */
ffp = &names[0]; /* scan for matches */
while (ffp->n_func != NULL) {
if (strncmp(buf, ffp->n_name, strlen(buf)) == 0) {
/* a possible match! More than one? */
if ((ffp + 1)->n_func == NULL ||
(strncmp(buf, (ffp+1)->n_name, strlen(buf)) != 0)) {
/* no...we match, print it */
sp = ffp->n_name + cpos;
while (*sp)
(*term.t_putchar)(*sp++);
(*term.t_flush)();
return(ffp->n_func);
} else {
/* << << << << << << << << << << << << << << << << << */
/* try for a partial match against the list */
/* first scan down until we no longer match the current input */
lffp = (ffp + 1);
while ((lffp+1)->n_func != NULL) {
if (strncmp(buf, (lffp+1)->n_name, strlen(buf)) != 0)
break;
++lffp;
}
/* and now, attempt to partial complete the string, char at a time */
while (TRUE) {
/* add the next char in */
buf[cpos] = ffp->n_name[cpos];
/* scan through the candidates */
cffp = ffp + 1;
while (cffp <= lffp) {
if (cffp->n_name[cpos] != buf[cpos])
goto onward;
++cffp;
}
/* add the character */
(*term.t_putchar)(buf[cpos++]);
}
/* << << << << << << << << << << << << << << << << << */
}
}
++ffp;
}
/* no match.....beep and onward */
(*term.t_beep)();
onward:;
(*term.t_flush)();
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
} else {
if (cpos < NSTRING-1 && c > ' ') {
buf[cpos++] = c;
(*term.t_putchar)(c);
}
++ttcol;
(*term.t_flush)();
}
}
}
kbdtext(buf) /* add this text string to the current keyboard macro
definition */
char *buf; /* text to add to keyboard macro */
{
/* if we are defining a keyboard macro, save it */
if (kbdmip != NULL) {
if (kbdmip+strlen(buf) > &kbdm[NKBDM-4]) {
ctrlg(FALSE, 0);
(*term.t_flush)();
return(ABORT);
}
/* copy string in and null terminate it */
while (*buf)
*kbdmip++ = *buf++;
*kbdmip++ = 0;
}
return(TRUE);
}